La exploración de datos nos permite verificar su calidad, entender los datos con los que trabajamos. Visualizar nuestros datos es un buen comienzo, pero a menudo vamos a necesitar transformar los datos previamente.
Formato tidy data (long)
Cada variable tiene su propia columna - cada observación tiene su propia fila - cada valor tiene su propia celda
Ejemplo organización “tidy”
Buenas prácticas en el manejo de datos
Las variables van en columnas (e.g. mediciones: altura, peso, sexo)
Tabla del análisis de la varianza
Las observaciones cada una en una fila (e.g. individuos de pingüinos)
Evitar espacios, números, y caracteres especiales en los nombres de columnas.
Siempre anotar valores de cero, para diferenciarlos de datos faltantes.
Usar celdas vacías o con NA para datos faltantes.
Las fechas incluirlas en columnas separadas como year, month, day.
No combinar varias informaciones en una misma celda. EJEMPLOS
Realiza todas las manipulaciones de datos mediante código para dejar constancia de los cambios.
Exporta los datos como texto plano (txt, csv)
Organizacion tabla
Hertz & McNeill 2024
dplyr y ggplot
coomo funcionan, libreria tidyverse.
Pipes: cómo encadenar operaciones
Los “pipes” permiten escribir código paso a paso, de forma legible:
library(dplyr)# El propio de dplyriris %>%filter(Sepal.Length >5) %>%summarise(promedio =mean(Sepal.Width))
promedio
1 3.048305
# (control (command) + shift + M)# el de R desde hace dos añosiris |>filter(Sepal.Length >5) |>summarise(promedio =mean(Sepal.Width))
# A tibble: 6 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
<fct> <fct> <dbl> <dbl> <int> <int>
1 Adelie Torgersen 39.1 18.7 181 3750
2 Adelie Torgersen 39.5 17.4 186 3800
3 Adelie Torgersen 40.3 18 195 3250
4 Adelie Torgersen NA NA NA NA
5 Adelie Torgersen 36.7 19.3 193 3450
6 Adelie Torgersen 39.3 20.6 190 3650
# ℹ 2 more variables: sex <fct>, year <int>
str(datos)
tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
$ species : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
$ island : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
$ bill_length_mm : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
$ bill_depth_mm : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
$ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
$ body_mass_g : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
$ sex : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
$ year : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
summary(datos)
species island bill_length_mm bill_depth_mm
Adelie :152 Biscoe :168 Min. :32.10 Min. :13.10
Chinstrap: 68 Dream :124 1st Qu.:39.23 1st Qu.:15.60
Gentoo :124 Torgersen: 52 Median :44.45 Median :17.30
Mean :43.92 Mean :17.15
3rd Qu.:48.50 3rd Qu.:18.70
Max. :59.60 Max. :21.50
NA's :2 NA's :2
flipper_length_mm body_mass_g sex year
Min. :172.0 Min. :2700 female:165 Min. :2007
1st Qu.:190.0 1st Qu.:3550 male :168 1st Qu.:2007
Median :197.0 Median :4050 NA's : 11 Median :2008
Mean :200.9 Mean :4202 Mean :2008
3rd Qu.:213.0 3rd Qu.:4750 3rd Qu.:2009
Max. :231.0 Max. :6300 Max. :2009
NA's :2 NA's :2
library(here)library(tidyverse)library(tm)library(wordcloud2)library(readxl)# se puede importar tablas desde excel, csv, ggsheets, txt...datos_curso =read_excel(here("data/datos-curso.xlsx"))# ifselse en dplyr?# Vemos un resumenstr (datos_curso)
tibble [19 × 15] (S3: tbl_df/tbl/data.frame)
$ Id : num [1:19] 1 2 3 4 5 6 7 8 9 10 ...
$ Hora de inicio : POSIXct[1:19], format: "2025-10-14 08:29:14" "2025-10-14 08:42:03" ...
$ Hora de finalización : POSIXct[1:19], format: "2025-10-14 08:37:19" "2025-10-14 08:44:21" ...
$ Correo electrónico : chr [1:19] "anonymous" "anonymous" "anonymous" "anónimo" ...
$ Nombre : logi [1:19] NA NA NA NA NA NA ...
$ Comentarios: DNI o documento : logi [1:19] NA NA NA NA NA NA ...
$ Curso de la primera matrícula en el programa de doctorado : chr [1:19] "2024-25" "2023-24" "2022-23" "2023-24" ...
$ Puntos: Curso de la primera matrícula en el programa de doctorado : num [1:19] 0 0 0 NA NA NA NA NA NA NA ...
$ Comentarios: Curso de la primera matrícula en el programa de doctorado: logi [1:19] NA NA NA NA NA NA ...
$ Cuéntanos tu experiencia con R : chr [1:19] "He usado R ya en anteriores ocasiones, tengo una pequeña base de conocimientos y estoy familiarizada con el lenguaje." "Usé R en el máster y para hacer algunos gráficos durante el principio de la tesis, pero me falta mucha segurida"| __truncated__ "Para mi tesis en el programa he debido desarrollar un Tn-seq, personalmente me ha costado horas de trabajo crea"| __truncated__ "He utilizado R en algunas asignaturas del grado y del máster, pero de una manera superficial." ...
$ Puntos: Cuéntanos tu experiencia con R : num [1:19] 0 0 0 NA NA NA NA NA NA NA ...
$ Comentarios: Cuéntanos tu experiencia con R : logi [1:19] NA NA NA NA NA NA ...
$ Observaciones : chr [1:19] NA NA NA NA ...
$ Puntos: Observaciones : num [1:19] 0 0 0 NA NA NA NA NA NA NA ...
$ Comentarios: Observaciones : logi [1:19] NA NA NA NA NA NA ...
#View (datos_curso) # Se nos abre en Rstudio
colnames(datos_curso) # Miramos las columnas que tiene
[1] "Id"
[2] "Hora de inicio"
[3] "Hora de finalización"
[4] "Correo electrónico"
[5] "Nombre"
[6] "Comentarios: DNI o documento"
[7] "Curso de la primera matrícula en el programa de doctorado"
[8] "Puntos: Curso de la primera matrícula en el programa de doctorado"
[9] "Comentarios: Curso de la primera matrícula en el programa de doctorado"
[10] "Cuéntanos tu experiencia con R"
[11] "Puntos: Cuéntanos tu experiencia con R"
[12] "Comentarios: Cuéntanos tu experiencia con R"
[13] "Observaciones"
[14] "Puntos: Observaciones"
[15] "Comentarios: Observaciones"
datos_curso_filtrado <- datos_curso |>rename (curso ="Curso de la primera matrícula en el programa de doctorado",experiencia ="Cuéntanos tu experiencia con R",comentarios ="Observaciones",email ="Correo electrónico") |>select (curso, experiencia, comentarios, email) |>mutate (email =str_replace(email, "anonymous", "anónimo")) |>filter (curso =="2022-23") # filtrado y sin filtrar otra diapo.words <-tolower(unlist(strsplit(datos_curso_filtrado$experiencia, " ") ) ) words <-gsub("[[:punct:][:digit:]]", "",words)stop_es <-stopwords("spanish")words_clean <- words[!words %in% stop_es]# Plotwordcloud2(data=table(words_clean), size=0.5,color =rep(RColorBrewer::brewer.pal(11, "PRGn"), 20))
ggplot(mtcars, aes(x = wt, y = mpg)) +geom_point()
De simple
ggplot(data = datos_limpios_kg, aes(x = bill_length_mm, y = bill_depth_mm)) +geom_point()
A complejo
library(ggplot2)p <-ggplot(data = penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +geom_point(aes(color = body_mass_g), alpha = .6) + scico::scale_color_scico(palette ="bamako", direction =-1)+## custom axes scalingscale_x_continuous(breaks =3:6*10, limits =c(30, 60)) +scale_y_continuous(breaks =seq(12.5, 22.5, by =2.5), limits =c(12.5, 22.5)) +## custom labelslabs(title ='Bill Dimensions of Brush-Tailed Penguins (Pygoscelis)',subtitle ='A scatter plot of bill depth versus bill length.',caption ='data = Gorman, Williams & Fraser (2014) PLoS ONE, graph = Cédric Scherer',x ='Bill Length (mm)',y ='Bill Depth (mm)',color ='Body mass (g)') +theme_minimal () p
Bases de ggplot
ggplot = Grammar of Graphics (GG) Separa cada componente de un gráfico en componentes individuales, creando distintas capas. Así tenemos resultados más flexibles y personalizables.
Data
Aesthetics (x, y)
Geometries (points, lines, boxplots)
Scales (colores)
Facets (subplots)
Statistics (show means, counts, and other statistical summaries of data)
Coordinates
Theme (apariencia general del gráfico)
layers
Ejemplo de capas
Hertz & McNeill 2024
Aesthetics
x, y
colour: color de los geoms
fill: el color de dentro de los geoms
group: a qué grupo pertenece un geom
shape: la forma de los puntos
linetype: tipo de linea (solid, dashed, etc)
size: escalar el tamaño
alpha: transparencia
Geometries
Categórico
geom_bar(): bar charts for categorical x axis
geom_boxplot(): box and whiskers plot for categorical variables
Continuo
geom_histogram(): histogram for continuous x axis
geom_violin(): distribution kernel of data dispersion
geom_point(): scatterplot
geom_line(): lines connecting points by increasing value of x
geom_path(): lines connecting points in sequence of appearance
Facets
facet_wrap()
facet_grid()
facet_wrap(~ variable) | Divide el gráfico en varios paneles según una sola variable. Coloca los paneles en una cuadrícula flexible (se “envuelven” automáticamente).
facet_grid(fila ~ columna) | Divide los gráficos según dos variables: una define las filas y otra las columnas.
Empecemos a visualizar
Creamos una base de datos simple sobre tipos de comida y cuanto cuestan
library(ggplot2)library(dplyr)dat <-data.frame(meal =factor(c("Breakfast", "Lunch", "Dinner", "Dinner", "Lunch")),total_bill =c(4.89, 7.23, 15.34, 21.23, 11.32))# Very basic bar graphdat %>%ggplot(aes(x=meal, y=total_bill)) +geom_bar(stat="identity")
Queremos el tipo de comida en distintos colores
dat %>%ggplot(aes(x=meal, y=total_bill, fill=meal)) +geom_bar(stat="identity")
¿Qué pasa si modificamos “color”?
dat %>%ggplot(aes(x=meal, y=total_bill, color=meal)) +geom_bar(stat="identity")
Sin leyenda -> guides (fill=FALSE) Añadimos
dat %>%ggplot(aes(x=meal, y=total_bill, fill=meal)) +geom_bar(stat="identity") +guides(fill=FALSE)
Añadimos el labs y theme
dat %>%ggplot(aes(x=meal, y=total_bill, fill=meal)) +geom_bar(stat="identity") +guides(fill=FALSE) +labs(title ="Precio medio por comida", subtitle ="Ajustado para dos personas",x ="Tipo de comida",y ="Precio") +theme_minimal()